<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<link rel="stylesheet" type="text/css" href="${/META-INF/css/THEME.css}">
</head>
<body>
<script src="${/META-INF/js/d3.min.js}"></script>
<script src="${/META-INF/js/pp.js}"></script>
<script>
window.addEventListener('load', createFlare);

function createFlare(){	
pp.onResizeEnd(function() {
  width = pp.getInnerWidth();
  height = pp.getInnerHeight();
  radius = (Math.min(width, height) / 2) - 10;

  d3.select("svg")
    .attr("width", width)
    .attr("height", height);

  svg.attr("transform", "translate(" + width / 2 + "," + (height / 2) + ")");

  flare.click(node);
});

var width = pp.getInitialWidth(),
    height = pp.getInitialHeight(),
    radius = (Math.min(width, height) / 2) - 10;

var x = d3.scale.linear()
    .range([0, 2 * Math.PI]);

var y = d3.scale.linear()
    .range([0, radius]);

var tooltip = d3.select("body").append("div")
    .attr("class", "tooltip")
    .style("opacity", 0);

var svg = d3.select("body").append("svg")
    .attr("width", width)
    .attr("height", height)
    .append("g")
    .attr("transform", "translate(" + width / 2 + "," + (height / 2) + ")");

var partition = d3.layout.partition()
    .sort(null)
    .value(function(d) { return 1; });

var arc = d3.svg.arc()
    .startAngle(function(d) { return Math.max(0, Math.min(2 * Math.PI, x(d.x))); })
    .endAngle(function(d) { return Math.max(0, Math.min(2 * Math.PI, x(d.x + d.dx))); })
    .innerRadius(function(d) { return Math.max(0, y(d.y)); })
    .outerRadius(function(d) { return Math.max(0, y(d.y + d.dy)); });

// Keep track of the node that is currently being displayed as the root.
var node;

var flare = {
  setContent: function(content) {
    root = JSON.parse(content);
    node = root;

    this.path = svg.datum(root).selectAll("path")
      .data(partition.value(function(d) { return d.value; }).nodes)
      .enter()
      .append("path")
      .attr("d", arc)
      .style("fill", function(d) { return d.color })
      .on("click", this.click)
      .on("mouseover", this.mouseover)
      .on("mousemove", this.mousemove)
      .on("mouseout", this.mouseout)
      .each(stash);

    this.text = svg.datum(root)
      .selectAll("text")
      .data(partition.value(function(d) { return d.value; }).nodes)

    var textEnter = this.text.enter().append("text")
      .style("fill-opacity", 1)
      .style("fill", function(d) {
        return brightness(d3.rgb(d.color)) < 125 ? "#eee" : "#000";
      })
      .attr("text-anchor", function(d) {
        return x(d.x + d.dx / 2) > Math.PI ? "end" : "start";
      })
      .attr("dy", ".2em")
      .attr("transform", function(d) {
        var angle = x(d.x + d.dx / 2) * 180 / Math.PI - 90;
        return "rotate(" + angle + ")translate(" + (y(d.y) + 5) + ")rotate(" + (angle > 90 ? -180 : 0) + ")";
      })
      .on("click", this.click)
      .on("mouseover", this.mouseover)
      .on("mousemove", this.mousemove)
      .on("mouseout", this.mouseout);

    textEnter.append("tspan")
      .attr("x", 0)
      .attr("visibility", function(d) { return isBigEnough(d) ? null : "hidden" })
      .text(cropped);
  },

  click: function(d) {
	if (!d.caption)
	    return;

    node = d;

    if (typeof onItemSelected === 'function') {
      onItemSelected(d.uuid);
    }

    flare.path.transition()
      .duration(1000)
      .attrTween("d", arcTweenZoom(d));

    // Somewhat of a hack as we rely on arcTween updating the scales.
    flare.text.style("visibility", function(e) {
          return isBigEnough(e) && isParentOf(d, e) ? null : d3.select(this).style("visibility");
        })
      .transition()
      .duration(1000)
      .attrTween("text-anchor", function(d) {
          return function() {
            return x(d.x + d.dx / 2) > Math.PI ? "end" : "start";
          };
      })
      .attrTween("transform", function(d) {
          return function() {
            var angle = x(d.x + d.dx / 2) * 180 / Math.PI - 90;
            return "rotate(" + angle + ")translate(" + (y(d.y) + 5) + ")rotate(" + (angle > 90 ? -180 : 0) + ")";
          };
        })
      .style("fill-opacity", function(e) { return isParentOf(d, e) ? 1 : 1e-6; })
      .each("end", function(e) {
          d3.select(this)
            .style("visibility", isBigEnough(e) && isParentOf(d, e) ? null : "hidden")
            .text(cropped(e));
      });
  },

  mouseover: function(d) {
    if (!d.caption)
      return;

    tooltip.transition()
      .duration(250)
      .style("opacity", 1);

    tooltip.html(d.caption)
        .style("left", (d3.event.pageX + 6) + "px")
        .style("top", (d3.event.pageY - 20) + "px");
  },

  mousemove: function(d) {
	if (!d.caption)
	    return;

    tooltip
        .style("left", (d3.event.pageX + 6) + "px")
        .style("top", (d3.event.pageY - 20) + "px");
  },

  mouseout: function(d) {
    tooltip.transition()
      .duration(250)
      .style("opacity", 0);
  }
}

function cropped(d) {
  if (!d.depth)
    return "";

  var available = (Math.max(0, y(d.y + d.dy)) - Math.max(0, y(d.y))) / 7;
  if (d.name.length <= available)
    return d.name;
  else
    return d.name.substring(0, available) + "...";
}

function isBigEnough(d) {
  var startAngle = Math.max(0, Math.min(2 * Math.PI, x(d.x)));
  var endAngle = Math.max(0, Math.min(2 * Math.PI, x(d.x + d.dx)));
  return d.depth && (endAngle - startAngle) > (2 * Math.PI / 100);
}

// Setup for switching data: stash the old values for transition.
function stash(d) {
  d.x0 = d.x;
  d.dx0 = d.dx;
}

// When switching data: interpolate the arcs in data space.
function arcTweenData(a, i) {
  var oi = d3.interpolate({x: a.x0, dx: a.dx0}, a);
  function tween(t) {
    var b = oi(t);
    a.x0 = b.x;
    a.dx0 = b.dx;
    return arc(b);
  }
  if (i == 0) {
   // If we are on the first arc, adjust the x domain to match the root node
   // at the current zoom level. (We only need to do this once.)
    var xd = d3.interpolate(x.domain(), [node.x, node.x + node.dx]);
    return function(t) {
      x.domain(xd(t));
      return tween(t);
    };
  } else {
    return tween;
  }
}

// When zooming: interpolate the scales.
function arcTweenZoom(d) {
  var xd = d3.interpolate(x.domain(), [d.x, d.x + d.dx]),
      yd = d3.interpolate(y.domain(), [d.y, 1]),
      yr = d3.interpolate(y.range(), [d.y ? 20 : 0, radius]);
  return function(d, i) {
    return i
        ? function(t) { return arc(d); }
        : function(t) { x.domain(xd(t)); y.domain(yd(t)).range(yr(t)); return arc(d); };
  };
}

function isParentOf(p, c) {
  if (p === c) return true;
  if (p.children) {
    return p.children.some(function(d) {
      return isParentOf(d, c);
    });
  }
  return false;
}

// http://www.w3.org/WAI/ER/WD-AERT/#color-contrast
function brightness(rgb) {
  return rgb.r * .299 + rgb.g * .587 + rgb.b * .114;
}

flare.setContent(loadData());
}
</script>
</body>
</html>
